package com.eastfair.venuebooking.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.eastfair.boot.request.PageParams;
import com.eastfair.boot.service.SuperServiceImpl;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.base.entity.SuperEntity;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.exception.BizException;
import com.eastfair.venuebooking.dao.QuotationMapper;
import com.eastfair.venuebooking.dto.QuotationDTO;
import com.eastfair.venuebooking.dto.QuotationDetailDTO;
import com.eastfair.venuebooking.dto.QuotationQuery;
import com.eastfair.venuebooking.entity.ContractManage;
import com.eastfair.venuebooking.entity.Exhibition;
import com.eastfair.venuebooking.entity.Quotation;
import com.eastfair.venuebooking.entity.QuotationDetail;
import com.eastfair.venuebooking.enumeration.ExhibitionStateEnum;
import com.eastfair.venuebooking.exceptioncode.VenueBookingExceptionCode;
import com.eastfair.venuebooking.service.*;
import com.eastfair.venuebooking.vo.BusinessOpportunityVO;
import com.eastfair.venuebooking.vo.PriceListSpaceVO;
import com.eastfair.venuebooking.vo.QuotationDetailVO;
import com.eastfair.venuebooking.vo.QuotationVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 报价书
 * </p>
 *
 * @author linan
 * @date 2022-06-08
 */
@Slf4j
@Service
public class QuotationServiceImpl extends SuperServiceImpl<QuotationMapper, Quotation> implements QuotationService {

    @Resource
    private QuotationMapper quotationMapper;

    @Resource
    private QuotationDetailService quotationDetailService;

    @Resource
    private ExhibitionService exhibitionService;

    @Resource
    private BusinessOpportunityService businessOpportunityService;

    @Resource
    private ContractManageService contractManageService;

    @Resource
    private ExhibitionSpaceService exhibitionSpaceService;

    @Resource
    private VenueBookingUserAccountService venueBookingUserAccountService;

    /**
     * 处理逻辑删除相关处理
     *
     * @param modelList 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerRemoveByIdsOfLogic(Collection<Quotation> modelList) {
        modelList.forEach(s -> s.setIsDeleted(BusinessConstant.YES));
        return R.successDef();
    }

    @Override
    protected R<Boolean> handlerSave(Quotation model) {
        model.setProjectId(ContextUtil.getProjectId());
        model.setTenantId(ContextUtil.getTenantId());
        model.setSubsystemId(ContextUtil.getSubSystemId());
        return R.successDef();
    }

    @Override
    public IPage<QuotationVO> listQuotationVOs(PageParams<QuotationQuery> pageParams) {
        log.info("listQuotationVOs - 分页查询报价记录, pageParams={}", pageParams);

        Page<Quotation> page = pageParams.buildPage();
        QuotationQuery query = pageParams.getModel();
        QueryWrapper<Quotation> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("q.is_deleted", BusinessConstant.NO)
                .eq(query.getIsEnabled() != null, "q.is_enabled", query.getIsEnabled())
                .eq(query.getId() != null, "q.id", query.getId())
                .eq(query.getExhibitionId() != null, "q.exhibition_id", query.getExhibitionId())
                .eq(query.getOpportunityId() != null, "q.opportunity_id", query.getOpportunityId())
                .like(!StrUtil.isBlank(query.getQuotationTitle()), "q.quotation_title", query.getQuotationTitle());
        ;
        IPage<QuotationVO> voPage = quotationMapper.listQuotationVOs(page, queryWrapper);
        // 查询报价人信息
        List<QuotationVO> quotationVOList = voPage.getRecords();
        if (quotationVOList == null || quotationVOList.isEmpty()) {
            return voPage;
        }
        List<Long> createByIds = quotationVOList
                .stream()
                .map(QuotationVO::getCreatedBy)
                .collect(Collectors.toList());
        Map<Long, String> userMap = venueBookingUserAccountService.listUserAccount(createByIds);

        quotationVOList.forEach(quotationVO -> {
            String createdByName = userMap.get(quotationVO.getCreatedBy());
            quotationVO.setCreateByName(createdByName);
        });

        return voPage;
    }

    @Override
    public List<PriceListSpaceVO> listPriceListSpacesByQuotationId(Long quotationId) {
        log.info("listPriceListSpacesByQuotationId - 通过报价书id查询场地价目, quotationId={}", quotationId);
        return quotationMapper.listPriceListSpacesByQuotationId(quotationId);
    }

    @Override
    public List<QuotationVO> listForContractManage(Long exhibitionId) {
        log.info("listForContractManage - 查询可用于添加合同的报价书列表, exhibitionId={}", exhibitionId);
        List<ContractManage> contractManageList = contractManageService.listContractManageByExhibitionId(exhibitionId);
        // 已经生成了合同的报价书ID列表
        List<Long> hasCreateContractQuotationIdList = null;
        if (contractManageList != null && !contractManageList.isEmpty()) {
            hasCreateContractQuotationIdList = contractManageList
                    .stream()
                    .map(ContractManage::getQuotationId)
                    .collect(Collectors.toList());
        }
        QuotationDTO query = new QuotationDTO();
        query.setExhibitionId(exhibitionId);
        query.setHasCreateContractQuotationIdList(hasCreateContractQuotationIdList);
        List<Quotation> list = listQuotation(query);
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        return list
                .stream()
                .map(quotation -> BeanUtil.toBean(quotation, QuotationVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public List<Quotation> listQuotationsByExhibitionId(Long exhibitionId) {
        log.info("listQuotationsByExhibitionId - 展会ID查询报价书列表, exhibitionId={}", exhibitionId);
        QuotationDTO query = new QuotationDTO();
        query.setExhibitionId(exhibitionId);
        return listQuotation(query);
    }

    @Override
    public List<Quotation> listQuotation(QuotationDTO quotationDTO) {
        QueryWrapper<Quotation> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(Quotation::getIsDeleted, BusinessConstant.DELETE_NO)
                .eq(Quotation::getIsEnabled, BusinessConstant.ENABLE_YES)
                .eq(quotationDTO.getExhibitionId() != null, Quotation::getExhibitionId, quotationDTO.getExhibitionId())
                .notIn(quotationDTO.getHasCreateContractQuotationIdList() != null && !quotationDTO.getHasCreateContractQuotationIdList().isEmpty(),
                        Quotation::getId, quotationDTO.getHasCreateContractQuotationIdList())
        ;
        return list(queryWrapper);
    }

    @Override
    public QuotationVO getQuotationVOById(Long id) {
        log.info("getQuotationVOById - id 查询详情, id={}", id);
        Quotation quotation = getById(id);
        if (quotation == null) {
            throw BizException.wrap(VenueBookingExceptionCode.QUO_NOT_EXIST);
        }
        QuotationVO quotationVO = BeanUtil.toBean(quotation, QuotationVO.class);
        // 查询报价书详情列表
        List<QuotationDetailVO> quotationDetailVOList = quotationDetailService.listQuotationDetailVOsByQuotationId(id);
        quotationVO.setQuotationDetailList(quotationDetailVOList);
        quotationVO.setSpaceNum(quotationDetailVOList.size());
        return quotationVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveQuotation(QuotationDTO quotationDTO) {
        log.info("saveQuotation - 保存报价书, quotationDTO={}", quotationDTO);

        Quotation quotation = BeanUtil.toBean(quotationDTO, Quotation.class);
        quotation.setSpaceAmount(quotation.getDiscountAmount().subtract(quotation.getServiceBondAmount()).subtract(quotation.getSplaceBondAmount()));
        boolean saveQuotation = save(quotation);
        if (saveQuotation) {
            // 如果是展会添加了报价书
            if (Objects.nonNull(quotation.getExhibitionId())) {
                // 修改展会状态
                exhibitionService.updateExhibitionState(quotation.getExhibitionId(), ExhibitionStateEnum.STATE_THREE);
                // 保存展会场地关系
                exhibitionSpaceService.saveBatchByQuotationDetailList(quotationDTO.getQuotationDetailList(), quotationDTO.getExhibitionId());
            }
            return quotationDetailService.saveBatch(quotationDTO.getQuotationDetailList(), quotation.getId());
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveExhibitionQuotation(QuotationDTO quotationDTO) {
        log.info("saveExhibitionQuotation - 新增展会报价, quotationDTO={}", quotationDTO);
        QuotationVO quotationVO = getQuotationVOById(quotationDTO.getId());
        if (quotationVO == null) {
            return false;
        }
        CopyOptions copyOptions = CopyOptions.create()
                .ignoreNullValue()
                .setIgnoreProperties("id", "opportunityId", "quotationDetailList");
        // 报价书
        QuotationDTO saveQuotationDTO = BeanUtil.toBean(quotationVO, QuotationDTO.class, copyOptions);
        saveQuotationDTO.setExhibitionId(quotationDTO.getExhibitionId());
        // 报价书详情列表
        List<QuotationDetailVO> quotationDetailList = quotationVO.getQuotationDetailList();
        if (quotationDetailList != null && !quotationDetailList.isEmpty()) {
            List<QuotationDetailDTO> quotationDetailDTOList = quotationDetailList
                    .stream()
                    .map(quotationDetailVO -> BeanUtil.toBean(quotationDetailVO, QuotationDetailDTO.class, copyOptions))
                    .collect(Collectors.toList());
            saveQuotationDTO.setQuotationDetailList(quotationDetailDTOList);
        }
        return saveQuotation(saveQuotationDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateQuotation(QuotationDTO quotationDTO) {
        log.info("updateQuotation - 更新报价书, quotationDTO={}", quotationDTO);
        // 保存报价书
        Quotation quotation = BeanUtil.toBean(quotationDTO, Quotation.class);
        quotation.setSpaceAmount(quotation.getDiscountAmount().subtract(quotation.getServiceBondAmount()).subtract(quotation.getSplaceBondAmount()));
        updateById(quotation);
        List<Long> quotationIds = new ArrayList<>(16);
        quotationIds.add(quotationDTO.getId());
        if (quotation.getExhibitionId() != null) {
            // 清空展会场地
            exhibitionSpaceService.removeBatchByQuotationIds(quotationIds);
            // 保存展会场地
            exhibitionSpaceService.saveBatchByQuotationDetailList(quotationDTO.getQuotationDetailList(), quotationDTO.getExhibitionId());
        }
        // 清空报价书详情
        quotationDetailService.removeByQuotationIds(quotationIds);
        // 保存报价书详情
        return quotationDetailService.saveBatch(quotationDTO.getQuotationDetailList(), quotation.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeQuotationByIds(List<Long> ids) {
        if (contractManageService.existContractManageByQuotationIds(ids)) {
            throw BizException.wrap(VenueBookingExceptionCode.CONTRACT_EXIST_CAN_NOT_DELETE_QUOTATION);
        }
        // 删除展会场地
        exhibitionSpaceService.removeBatchByQuotationIds(ids);
        // 删除报价书详情
        boolean remove = quotationDetailService.removeByQuotationIds(ids);
        // 删除报价书
        if (remove) {
            List<Quotation> quotations = listByIds(ids);
            boolean b = removeByIdsOfLogic(quotations);
            if(b){
                // 根据是否存在报价书，更新展会状态
                updateExhibitionState(ids);
            }
        }
        return false;
    }

    /**
     * 根据是否存在报价书，更新展会状态
     * @param ids
     */
    private void updateExhibitionState(List<Long> ids) {
        List<Quotation> list = list(new QueryWrapper<Quotation>().lambda().in(SuperEntity::getId, ids));
        Set<Long> exhibitionIds = list.stream().map(Quotation::getExhibitionId).collect(Collectors.toSet());
        if(CollectionUtils.isNotEmpty(exhibitionIds)){
            for (Long exhibitionId : exhibitionIds) {
                QuotationDTO quotationDTO = new QuotationDTO();
                quotationDTO.setExhibitionId(exhibitionId);
                List<Quotation> quotationList = listQuotation(quotationDTO);
                if(CollectionUtils.isEmpty(quotationList)){
                    Exhibition exhibition = new Exhibition();
                    exhibition.setId(exhibitionId);
                    exhibition.setState(ExhibitionStateEnum.STATE_TWO);
                    exhibitionService.updateById(exhibition);
                }
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeQuotationDetail(QuotationDTO quotationDTO) {
        Quotation quotation = BeanUtil.toBean(quotationDTO, Quotation.class);
        quotation.setSpaceAmount(quotation.getDiscountAmount().subtract(quotation.getServiceBondAmount()).subtract(quotation.getSplaceBondAmount()));
        updateById(quotation);
        List<QuotationDetail> quotationDetails = quotationDetailService.listByIds(quotationDTO.getQuotationDetailIdList());
        return quotationDetailService.removeByIdsOfLogic(quotationDetails);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeQuotationByExhibitionId(Long exhibitionId) {
        log.info("removeQuotationByExhibitionId - 通过展会ID移除报价书, exhibitionId={}", exhibitionId);
        List<Quotation> quotations = listQuotationsByExhibitionId(exhibitionId);
        if (quotations == null || quotations.isEmpty()) {
            return false;
        }
        List<Long> ids = quotations
                .stream()
                .map(SuperEntity::getId)
                .collect(Collectors.toList());
        return removeQuotationByIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateQuotationDisableByIds(List<Long> ids) {
        log.info("updateQuotationDisableByIds - 批量ID更新报价书为失效, ids={}", ids);
        // 移除展会预定的场地
        exhibitionSpaceService.removeBatchByQuotationIds(ids);
        // 报价书失效
        List<Quotation> quotations = listByIds(ids);
        quotations.forEach(quotation -> quotation.setIsEnabled(BusinessConstant.ENABLE_NO));
        updateBatchById(quotations);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateQuotationDisableByExhibitionId(Long exhibitionId) {
        log.info("updateQuotationDisableByExhibitionId - 展会ID更新报价书为失效, exhibitionId={}", exhibitionId);
        List<Quotation> quotations = listQuotationsByExhibitionId(exhibitionId);
        if (quotations == null || quotations.isEmpty()) {
            return false;
        }
        List<Long> ids = quotations
                .stream()
                .map(SuperEntity::getId)
                .collect(Collectors.toList());
        return updateQuotationDisableByIds(ids);
    }

    @Override
    public Map<String, Object> validQuotationPriceDuration(QuotationDTO quotationDTO) {
        log.info("validQuotationPriceDuration - 验证报价书计价时长, quotationDTO={}", quotationDTO);

        Exhibition verifyingExhibition = getVerifyingExhibitionByQuotation(quotationDTO);
        LocalDateTime arrangementStartTime = verifyingExhibition.getArrangementStartTime();
        LocalDateTime dismantlingEndTime = verifyingExhibition.getDismantlingEndTime();
        if (arrangementStartTime == null || dismantlingEndTime == null) {
            throw BizException.validFail("展会布展开始时间或撤展结束时间不可为空");
        }
        // 计算展会的办展天数
        long exhibitionPeriod = LocalDateTimeUtil.between(arrangementStartTime, dismantlingEndTime, ChronoUnit.DAYS) + 1;
        // 获取报价书详情中的最少计价时间
        boolean match = false;
        Map<String, Object> map = new HashMap<>(16);
        map.put("exhibitionPeriod", exhibitionPeriod);
        map.put("match", match);
        return map;
    }

    @Override
    public List<PriceListSpaceVO> queryAllSpace(QuotationQuery query) {
        PageParams<QuotationQuery> pageParams = new PageParams<QuotationQuery>();
        pageParams.setCurrent(1);
        pageParams.setSize(999999);
        pageParams.setModel(query);
        IPage<QuotationVO> quotationVOIPage = listQuotationVOs(pageParams);
        HashMap<Long, PriceListSpaceVO> map = new HashMap<>();
        for (QuotationVO record : quotationVOIPage.getRecords()) {
            List<PriceListSpaceVO> priceListSpaceVOS = listPriceListSpacesByQuotationId(record.getId());
            for (PriceListSpaceVO priceListSpaceVO : priceListSpaceVOS) {
                map.put(priceListSpaceVO.getSpaceId(), priceListSpaceVO);
            }
        }
        Collection<PriceListSpaceVO> values = map.values();
        List<PriceListSpaceVO> priceListSpaceVOS = new ArrayList<>(values);
        return priceListSpaceVOS;
    }

    /**
     * 获取待验证的展会(报价的商机关联的展会或者是报价的展会)，包含布展开始时间，撤展结束时间
     *
     * @param quotationDTO quotationDTO
     * @return Exhibition
     */
    private Exhibition getVerifyingExhibitionByQuotation(QuotationDTO quotationDTO) {
        Long exhibitionId = quotationDTO.getExhibitionId();
        Long opportunityId = quotationDTO.getOpportunityId();
        if (exhibitionId == null && opportunityId == null) {
            throw BizException.validFail("商机ID或展会ID不可为空");
        }
        // 报价的展会
        if (exhibitionId != null) {
            return exhibitionService.getById(exhibitionId);
        }
        // 报价的商机关联的展会
        BusinessOpportunityVO opportunityVO = businessOpportunityService.getBusinessOpportunityVOById(opportunityId);
        if (opportunityVO == null) {
            throw BizException.validFail(VenueBookingExceptionCode.BUS_NOT_EXIST.getMsg());
        }
        Exhibition exhibition = new Exhibition();
        exhibition.setArrangementStartTime(opportunityVO.getArrangementStartTime());
        exhibition.setDismantlingEndTime(opportunityVO.getDismantlingEndTime());
        return exhibition;
    }
}
